package com.iplay.service.user.userInfo;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.iplay.component.naming.UUIDNamingStrategy;
import com.iplay.dao.user.userInfo.UserBasicInfoDAO;
import com.iplay.dao.user.userInfo.UserCashbackInfoDAO;
import com.iplay.dto.ApiResponse;
import com.iplay.dto.ApiResponseMessage;
import com.iplay.dto.user.UserBasicInfoDTO;
import com.iplay.dto.user.UserCashbackInfoDTO;
import com.iplay.entity.user.UserDO;
import com.iplay.entity.user.userInfo.UserBasicInfoDO;
import com.iplay.entity.user.userInfo.UserCashbackInfoDO;
import com.iplay.service.exception.ResourceNotFoundException;
import com.iplay.service.storage.StorageService;
import com.iplay.service.user.UserService;
import com.iplay.web.resource.ResourcesUriBuilder;

@Service
public class UserInfoServiceImpl implements UserInfoService {
	@Autowired
	private UserCashbackInfoDAO userCashbackInfoDAO;

	@Autowired
	private UserBasicInfoDAO userBasicInfoDAO;

	@Autowired
	private UserService userService;

	@Autowired
	private StorageService storageService;

	@Autowired
	private UsernameUpdateService usernameUpdateService;

	private static final int DECIMAL = (int) Math.pow(10.0, 2.0);

	@Override
	public Optional<UserCashbackInfoDTO> findUserCashbackInfoByUserId(int id) {
		UserCashbackInfoDO findedDO = userCashbackInfoDAO.findOne(id);
		return Optional.ofNullable(transferDOToDTO(findedDO));
	}

	@Override
	public UserCashbackInfoDTO updateCashbackInfo(int userId, double completedCashbackChanges,
			double pendingCashbackChanges) {
		UserCashbackInfoDO findedDO = userCashbackInfoDAO.findOne(userId);
		if (findedDO != null) {
			completedCashbackChanges = round(completedCashbackChanges);
			pendingCashbackChanges = round(pendingCashbackChanges);
			findedDO.setTotalCashback(round(findedDO.getTotalCashback() 
					+ completedCashbackChanges + pendingCashbackChanges));
			findedDO.setCompletedCashback(round(findedDO.getCompletedCashback() + completedCashbackChanges));
			findedDO.setPendingCashback(round(findedDO.getPendingCashback() + pendingCashbackChanges));
			return transferDOToDTO(userCashbackInfoDAO.save(findedDO));
		}
		return null;
	}
	
	private double round(double d) {
		return (double)Math.round(d*DECIMAL)/DECIMAL;
	}

	@Override
	@Transactional
	public void initializeUserInfo(int userId) {
		saveUserCashbackInfo(new UserCashbackInfoDO(userId, 0, 0, 0));
		userBasicInfoDAO.save(new UserBasicInfoDO(userId, null, UserBasicInfoDAO.DEFAULT_AVATAR));
	}

	private UserCashbackInfoDTO transferDOToDTO(UserCashbackInfoDO userCashbackInfoDO) {
		UserCashbackInfoDTO dto = null;
		if (userCashbackInfoDO != null)
			dto = new UserCashbackInfoDTO(userCashbackInfoDO.getUserId(), userCashbackInfoDO.getTotalCashback(),
					userCashbackInfoDO.getCompletedCashback(), userCashbackInfoDO.getPendingCashback());
		return dto;
	}

	private UserCashbackInfoDO saveUserCashbackInfo(UserCashbackInfoDO userCashbackInfoDO) {
		return userCashbackInfoDAO.save(userCashbackInfoDO);
	}

	@Override
	@Transactional
	public boolean deleteUserInfo(int userId) {
		userCashbackInfoDAO.delete(userId);
		userBasicInfoDAO.delete(userId);
		return true;
	}

	@Override
	public ApiResponse<?> updateAvatar(int userId, MultipartFile file) {
		UserBasicInfoDO user = findUserBasicInfo(userId);
		String filename = storageService.store(file, UUIDNamingStrategy.generateUUID());
		user.setAvatar(filename);
		userBasicInfoDAO.save(user);
		return ApiResponse.createSuccessApiResponse(ResourcesUriBuilder.buildUri(filename));
	}

	@Override
	public ApiResponse<?> updatePhone(int userId, String phone) {
		UserBasicInfoDO user = findUserBasicInfo(userId);
		user.setPhone(phone);
		userBasicInfoDAO.save(user);
		return ApiResponse.createSuccessApiResponse(phone);
	}

	@Override
	public ApiResponse<?> updateEmail(int userId, String email) {
		UserDO user = findUser(userId);
		user.setEmail(email);
		if (userService.update(user).isPresent()) {
			return ApiResponse.createSuccessApiResponse(email);
		}
		return ApiResponse.createFailApiResponse(ApiResponseMessage.AUTH_EMAIL_OCCUPIED);
	}

	@Override
	public ApiResponse<?> updateUsername(int userId, String username) {
		UserDO user = findUser(userId);
		user.setUsername(username);
		if (userService.update(user).isPresent()) {
			usernameUpdateService.updateUsername(userId, username);
			return ApiResponse.createSuccessApiResponse(username);
		}
		return ApiResponse.createFailApiResponse(ApiResponseMessage.AUTH_USERNAME_OCCUPIED);
	}

	@Override
	public UserBasicInfoDTO findUserBasicInfoByUserId(int userId) {
		UserBasicInfoDO userBasicInfo = findUserBasicInfo(userId);
		UserDO user = findUser(userId);
		return new UserBasicInfoDTO(userId, user.getUsername(), ResourcesUriBuilder.buildUri(userBasicInfo.getAvatar()),
				user.getEmail(), userBasicInfo.getPhone());
	}

	private UserBasicInfoDO findUserBasicInfo(int userId) {
		UserBasicInfoDO user = userBasicInfoDAO.findOne(userId);
		if (user == null)
			throw new ResourceNotFoundException("User with id: " + userId + " does't exist");
		return user;
	}

	private UserDO findUser(int userId) {
		return userService.findById(userId)
				.orElseThrow(() -> new ResourceNotFoundException("User with id: " + userId + " does't exist"));
	}
}
